package com.jfinal.config;


import com.jfinal.kit.Prop;
import com.jfinal.kit.PropKit;
import com.jfinal.log.Log;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.cron4j.Cron4jPlugin;
import com.jfinal.plugin.druid.DruidPlugin;
import com.jfinal.plugin.ehcache.EhCachePlugin;
import com.jfinal.plugin.redis.RedisPlugin;
import com.jfinal.plugin.redisson.RedissonPlugin;
import java.lang.reflect.Method;
import java.util.List;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Protocol;


/**
 * @author CHEN
 */
public abstract class AutoJFinalConfig extends JFinalConfig {

    private static final Log LOG = Log.getLog(AutoJFinalConfig.class);

    private static final String CONFIG_PLUGIN_FAIL = "config %s fail ==> ";

    private static final String CONFIG_PLUGIN_SUCCESS = "config %s success ==> ";

    private static final String COULD_NOT_FIND_ANY_CONFIG = "could not find any config!";

    protected void configActiveRecordPlugin(Plugins plugins) {
        configActiveRecordPlugin(plugins, PropKit.getProp());
    }

    protected void configActiveRecordPlugin(Plugins plugins, Prop prop) {
        String mysql = "mysql";

        List<String> dbNames = ConfigKit.findConfigNames(prop, mysql, ".url");

        if (dbNames.isEmpty()) {
            loggingConfigPluginFail(ActiveRecordPlugin.class.getSimpleName(), COULD_NOT_FIND_ANY_CONFIG);
            return;
        }

        for (String dbName : dbNames) {
            String prefix = ConfigKit.createPrefix(mysql, dbName);
            DruidPlugin druidPlugin = DruidPluginKit.createByPrefix(prefix);

            //当数据库连接大于1个时，配置库名与连接url的库名必须保持一致
            if (dbNames.size() > 1) {
                checkDbNames(dbName, prefix);
            }

            plugins.add(druidPlugin);
            ActiveRecordPlugin activeRecordPlugin = new ActiveRecordPlugin(dbName, druidPlugin);
            activeRecordMapping(dbName, activeRecordPlugin);
            plugins.add((activeRecordPlugin));
            loggingConfigPluginSuccess(ActiveRecordPlugin.class.getSimpleName(), dbName);
        }
    }

    private void checkDbNames(String configName, String prefix) {
        String dbName = fetchDbName(PropKit.get(prefix + ".url"));
        if (!configName.equals(dbName)) {
            String message = String.format("configName[%s] not equals dbName[%s], please check config file!", configName, dbName);
            throw new RuntimeException(message);
        }
    }

    /**
     * 提取数据库名
     *
     * @param url
     * @return
     */
    private String fetchDbName(String url) {
        url = url.substring(0, url.indexOf("?"));
        String dbName = url.substring(url.lastIndexOf("/") + 1, url.length());
        return dbName;
    }

    protected void activeRecordMapping(String dbName, ActiveRecordPlugin activeRecordPlugin) {
        String className = PropKit.get("model.package") + "._" + dbName + "_MappingKit";
        try {
            Class<?> clazz = Class.forName(className);
            Method method = clazz.getMethod("mapping", ActiveRecordPlugin.class);
            method.invoke(null, activeRecordPlugin);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected void configRedisPlugin(Plugins plugins) {
        configRedisPlugin(plugins, PropKit.getProp());
    }

    protected void configRedisPlugin(Plugins plugins, Prop prop) {

        String redis = "redis";

        List<String> cacheNames = ConfigKit.findConfigNames(prop, redis, ".host");
        if (cacheNames.isEmpty()) {
            loggingConfigPluginFail(RedisPlugin.class.getSimpleName(), COULD_NOT_FIND_ANY_CONFIG);
            return;
        }

        for (String cacheName : cacheNames) {
            String prefix = ConfigKit.createPrefix(redis, cacheName);
            String host = PropKit.get(prefix + ".host");
            int port = PropKit.getInt(prefix + ".port", Protocol.DEFAULT_PORT);
            int database = PropKit.getInt(prefix + ".database", Protocol.DEFAULT_DATABASE);
            Integer timeout = PropKit.getInt(prefix + ".timeout", Protocol.DEFAULT_TIMEOUT);
            String password = PropKit.get(prefix + ".password", "");
            RedisPlugin redisPlugin = new RedisPlugin(cacheName, host, port, timeout, password, database);

            JedisPoolConfig jedisPoolConfig = redisPlugin.getJedisPoolConfig();
            Integer maxIdle = PropKit.getInt(prefix + ".maxIdle");
            if (maxIdle != null && maxIdle > 0) {
                jedisPoolConfig.setMaxIdle(maxIdle);
            }

            Integer minIdle = PropKit.getInt(prefix + ".minIdle");
            if (minIdle != null && minIdle > 0) {
                jedisPoolConfig.setMinIdle(minIdle);
            }

            Integer maxActive = PropKit.getInt(prefix + ".maxActive");
            if (maxActive != null && maxActive > 0) {
                jedisPoolConfig.setMaxTotal(maxActive);
            }

            plugins.add(redisPlugin);
            loggingConfigPluginSuccess(RedisPlugin.class.getSimpleName(), host + ":" + port + ":" + database);
        }

    }

    protected void configRedissonPlugin(Plugins plugins) {
        configRedissonPlugin(plugins, PropKit.getProp());
    }

    protected void configRedissonPlugin(Plugins plugins, Prop prop) {

        String redis = "redis";

        List<String> cacheNames = ConfigKit.findConfigNames(prop, redis, ".host");
        if (cacheNames.isEmpty()) {
            loggingConfigPluginFail(RedissonPlugin.class.getSimpleName(), COULD_NOT_FIND_ANY_CONFIG);
            return;
        }

        for (String cacheName : cacheNames) {
            String prefix = ConfigKit.createPrefix(redis, cacheName);
            String host = PropKit.get(prefix + ".host");
            int port = PropKit.getInt(prefix + ".port", Protocol.DEFAULT_PORT);
            int database = PropKit.getInt(prefix + ".database", Protocol.DEFAULT_DATABASE);
            String password = PropKit.get(prefix + ".password", "");

            // connectionPoolSize
            int maxActive = PropKit.getInt(prefix + ".maxActive", 64);

            plugins.add(new RedissonPlugin(cacheName, host, port, password, database, maxActive));
            loggingConfigPluginSuccess(RedissonPlugin.class.getSimpleName(), host + ":" + port + ":" + database);
        }

    }


    protected void configCron4jPlugin(Plugins plugins) {
        this.configCron4jPlugin(plugins, Cron4jPlugin.defaultConfigName + ".properties");
    }

    protected void configCron4jPlugin(Plugins plugins, String configFile) {
        configCron4jPlugin(plugins, new Prop(configFile));
    }

    protected void configCron4jPlugin(Plugins plugins, Prop prop) {
        plugins.add(new Cron4jPlugin(prop));
        loggingConfigPluginSuccess(Cron4jPlugin.class.getSimpleName(), "");

    }

    protected void configEhCachePlugin(Plugins plugins) {
        this.configEhCachePlugin(plugins, null);
    }

    protected void configEhCachePlugin(Plugins plugins, String configFile) {
        plugins.add((new EhCachePlugin(configFile)));
        loggingConfigPluginSuccess(EhCachePlugin.class.getSimpleName(), "");
    }

    private void loggingConfigPluginFail(String pluginName, String message) {
        LOG.warn(String.format(CONFIG_PLUGIN_FAIL, pluginName) + message);
    }

    private void loggingConfigPluginSuccess(String pluginName, String message) {
        LOG.info(String.format(CONFIG_PLUGIN_SUCCESS, pluginName) + message);
    }

}
